﻿using GetMan.Core.Extensions;
using GetMan.Core.Models;
using NLog;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;

namespace GetMan.Core.Helpers
{
    public static class ConfigHelper
    {
        private static readonly Logger _logger = LogManager.GetCurrentClassLogger();

        /// <summary>
        /// 加载XML配置文件
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="filePath"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public static IEnumerable<AddConfigModel> LoadXmlConfig(string fileName, string filePath = "")
        {
            try
            {
                if (filePath.IsNull())
                {
                    filePath = AppDomain.CurrentDomain.BaseDirectory;
                }
                string pathName = Path.Combine(filePath, fileName);
                if (!File.Exists(pathName))
                {
                    _logger.Info($"未找到[{pathName}]配置文件");
                    throw new Exception($"未找到[{pathName}]配置文件");
                }
                _logger.Info($"开始装载[{pathName}]配置文件");
                var doc = XDocument.Load(pathName);
                var settings = doc.Descendants("add")
                    .Select(x => new AddConfigModel
                    {
                        Key = x.Attribute("key")?.Value,
                        Value = x.Attribute("value")?.Value,
                        Des = x.Attribute("des")?.Value
                    });
                _logger.Info($"装载[{pathName}]配置文件成功");
                return settings.OrderByDescending(x => x.Key);
            }
            catch (Exception ex)
            {
                _logger.Info($"装载[{fileName}]配置文件异常");
                _logger.Info($"异常原因：{ex.Message}");
                throw;
            }
        }

        /// <summary>
        /// 绑定XML字段到对应静态类
        /// </summary>
        /// <param name="type"></param>
        /// <param name="fileName"></param>
        /// <param name="filePath"></param>
        public static void BindConfig<T>(string fileName = "", string filePath = "", bool inherit = false)
        {
            var type = typeof(T);
            if (fileName.IsNull())
                fileName = $"{type.Name}.xml";

            var settings = LoadXmlConfig(fileName, filePath);
            if (settings == null)
                return;
            PropertyInfo[] properties = type.GetProperties();
            foreach (PropertyInfo property in properties)
            {
                try
                {
                    var defaultAttr = (DefaultValueAttribute)Attribute.GetCustomAttribute(property, typeof(DefaultValueAttribute), inherit);
                    object defaultValue = defaultAttr?.Value;
                    var setting = settings.FirstOrDefault(x => string.Equals(x.Key, property.Name, StringComparison.CurrentCultureIgnoreCase));
                    if (setting == null || setting.Value.IsNull())
                    {
                        property.SetValue(null, defaultValue, null);
                        continue;
                    }

                    switch (Type.GetTypeCode(property.PropertyType))
                    {
                        case TypeCode.Int32:
                            property.SetValue(null, setting.Value.ToInt(), null);
                            break;
                        case TypeCode.Int64:
                            property.SetValue(null, setting.Value.ToLong(), null);
                            break;
                        case TypeCode.Double:
                            property.SetValue(null, setting.Value.ToDouble(), null);
                            break;
                        case TypeCode.Decimal:
                            property.SetValue(null, setting.Value.ToDecimal(), null);
                            break;
                        case TypeCode.Boolean:
                            property.SetValue(null, setting.Value.ToBool(), null);
                            break;
                        case TypeCode.DateTime:
                            property.SetValue(null, setting.Value.ToDateTime(), null);
                            break;
                        default:
                            property.SetValue(null, setting.Value, null);
                            break;
                    }
                }
                catch (Exception ex)
                {
                    _logger.Info($"配置文件{fileName},配置字段{property.Name}失败,失败原因：{ex.Message}");
                    throw;
                }
            }
        }
    }
}